﻿using System;
using System.Collections.Generic;
using System.Text;
using Core;

namespace Generators
{
    public abstract class CodeGenerator
    {
        protected ImplicitDefinition _ImDefination;
        protected Language _Language;

        public CodeGenerator(ImplicitDefinition id) 
        {
            _ImDefination = id;
            _Language = Language.CSharp; // default
        }

        /// <summary>
        /// Generate full code
        /// </summary>
        /// <returns></returns>
        public String Generate()
        {
            StringBuilder builder = new StringBuilder();

            builder.AppendLine(GenerateInputFunction());
            builder.AppendLine(GenerateOutputFunction());
            builder.AppendLine(GenerateMainFunction());

            return builder.ToString();
        }

        /// <summary>
        /// Generate code for input
        /// </summary>
        public String GenerateInputFunction()
        {
            StringBuilder code = new StringBuilder();

            code.AppendLine(DeclareInputFunction());
            code.AppendLine(BeginFunction());

            foreach (Argument arg in _ImDefination.Function.Arguments)
            {
                code.AppendLine(PromptInput(arg.Name));         // prompt user input
                code.AppendLine(ReadInput(arg.Name, arg.Type)); // read user input
            }           
            code.AppendLine(EndFunction());

            return code.ToString();
        }

        /// <summary>
        /// Generate code for output
        /// </summary>
        /// <returns></returns>
        public String GenerateOutputFunction()
        {
            StringBuilder code = new StringBuilder();

            code.AppendLine(DeclareOutputFunction());
            code.AppendLine(BeginFunction());
            code.AppendLine(WriteOutput());
            code.AppendLine(EndFunction());

            return code.ToString();
        }

        /// <summary>
        /// Generate main function
        /// </summary>
        /// <returns></returns>
        public String GenerateMainFunction()
        {
            StringBuilder code = new StringBuilder();

            code.AppendLine(DeclareMainFunction());
            code.AppendLine(BeginFunction());
            code.AppendLine("\t// Declare variables");
            foreach (Argument arg in _ImDefination.Function.Arguments)
            {
                code.AppendLine(DeclareVariable(arg.Type, arg.Name));
            }
            code.AppendLine(DeclareVariable(_ImDefination.Function.Result.Type, _ImDefination.Function.Result.Name));
            
            code.AppendLine("\t// Input");
            code.AppendLine(CallInputFunction());

            code.AppendLine("\t// Pre condition");
            code.AppendFormat("\tif({0})\n", _ImDefination.PreCond.ToString());
            code.AppendLine("\t" + BeginFunction());
            code.AppendLine("\t\t// Execute");
            code.AppendLine("\t" + CallThisFunction());

            code.AppendLine("\t\t// Output");
            code.AppendLine("\t" + CallOutputFunction());
            code.AppendLine("\t" + EndFunction());
            code.AppendLine(EndFunction());

            return code.ToString();
        }

        protected abstract String DeclareInputFunction();
        protected abstract String DeclareOutputFunction();
        protected abstract String DeclareMainFunction();

        protected abstract String CallInputFunction();
        protected abstract String CallOutputFunction();
        protected abstract String CallThisFunction();

        protected abstract String BeginFunction();
        protected abstract String EndFunction();
        protected abstract String DeclareVariable(DataType dataType, String varName);
        protected abstract String DeclareRefVariable(String dataType, String varName);
        protected abstract String PromptInput(String varName);
        protected abstract String WriteOutput();
        protected abstract String ReadInput(String varName, DataType dataType);        
    }
}
